/**
 * product build
 * @author dongls
 */
const NODE_ENV = 'production'
// 设置NODE_ENV
process.env.NODE_ENV = NODE_ENV

const path = require('path')
const webpack = require('webpack')
const shell = require('shelljs')
const { execSync } = require('child_process');
const publinkInjectHtmlUtil = require('publink-inject-html-log-util')
const colors = require('colors')

const webpackConfig = require('../config/webpack.spa.conf')

const BuildOption = require('./model/BuildOption')
const { GIT_EXIST, GIT_STATUS_PORCELAIN, GIT_CURRENT_BRANCH_COMMAND } = require('./model/command')
const { MONITOR_SCRIPT } = require('./model/const')

const { stringify } = require('./util/string')
const { getCurrentBranch } = require('./util/git')
const { exit, command } = require('./util/common')

const { notificationEnd, notificationStart } = require('./util/notification')

// 监控监本
const monitorScript = MONITOR_SCRIPT

/* 用户配置 */
const argv = require('./argv')
const argvData = argv(process.argv.slice(2))
const user = argvData.user || 'huangbc'
const userConfig = require(`./config/${user}`)

const isAnalyzer = argvData.analyzer; // 是否打包分析

console.log(`build start on ${ new Date().toLocaleString() }.`);

function productionSpaBuild(buildOption) {
  const config = webpackConfig(isAnalyzer)
  
  if (buildOption.isCloudBuild && argvData.noCache) {
    delete config.cache.allowCollectingMemory
  }
  
  webpack(config, function (err, stats) {
    
    if (err) {
      
      !isAnalyzer && notificationEnd('构建失败，请检查错误！！！')
      
      shell.exec('sleep 5')
      
      console.log(err);
      
      exit()
      
    }
    
    process.stdout.write(`${stats.toString({
      colors: true,
      modules: false,
      children: false,
      chunks: false,
      chunkModules: false
    }) }\n`);
    
    buildSuccessAfterHandler(buildOption)
    
  })
}

function productionViteSpaBuild(buildOption) {
  
  try {
    
    execSync(
      'npm run build:vite', 
      {
        encoding: 'utf8', 
        stdio: 'inherit' 
      }
    )
    
    buildSuccessAfterHandler(buildOption)
    
  } catch (error) {
    
    console.log(error)
    
    exit()
    
  }
  
}

function buildSuccessAfterHandler(buildOption = {}) {
  
  publinkInjectHtmlUtil({
    projectPath: './',
    buildingPath: 'dist',
    customInjectFunc(template) {
      return customInjectFunc(template);
    }
  })
  
  if(isAnalyzer) return
  
  // 服务端构建，切换web-resource仓库分支
  if (buildOption.isCloudBuild) {
    checkoutBranchWithWebResourceRepository(buildOption)
  }
  
  copyFile()
  
  checkIsBuildError(buildOption)
  
  // 服务端构建，提交代码到web-resource仓库
  if (buildOption.isCloudBuild) {
    commitCodeWithWebResourceRepository(buildOption)
  }
  
  notificationEnd()
}

/** 
 * @description 检查构建是否有错误
 * -- webpack 回调函数中回调参数 error 在构建失败时仍返回null, 没去看解决方法，暂时用这种方法
*/
function checkIsBuildError(buildOption) {
  
  // web-resource仓库地址
  const webResourceRepositoryPath = buildOption.path
  
  shell.cd(webResourceRepositoryPath)
  
  const output = shell.exec('git status -s', { silent: false })
  const gitStatusLines = output.stdout.trim().split('\n');
  
  const isAllDelete = gitStatusLines.every(line => line.includes('D '));
  
  if (isAllDelete) {
    !isAnalyzer && notificationEnd('构建失败，请检查错误！！！')
    console.log(`${colors.red('【ERROR】Caused: all files are deleted, is build error ')}`)
    exit()
  }
  
}

function checkoutBranchWithWebResourceRepository(buildOption) {
  // web-resource仓库地址
  const webResourceRepositoryPath = buildOption.path
  const branch = buildOption.branch
  
  if (!branch) {
    console.error('【ERROR】Caused: branch is empty')
    exit()
  }
  
  shell.cd(webResourceRepositoryPath)
  
  shell.exec('git checkout .')
  shell.exec('git stash')
  shell.exec('git checkout master')
  shell.exec('git fetch -p')
  shell.exec(`git branch -D ${branch}`)
  shell.exec(`git checkout ${branch}`)
  
  console.log(`checkout branch => ${branch}`)
  console.log(`checkout branch on ${new Date().toLocaleString()}`)
  console.log('web-resource 仓库分支列表')
  
  shell.exec('git branch')
}

/** 
 * @description 提交代码到web-resource仓库
 * @param {BuildOption} buildOption 构建配置
*/
function commitCodeWithWebResourceRepository(buildOption) {
  // web-resource仓库地址
  const webResourceRepositoryPath = buildOption.path
  
  shell.cd(webResourceRepositoryPath)
  
  shell.exec('git add --all')
  shell.exec('git add .')
  shell.exec(`git commit -am "build: ${buildOption.branch} 分支 构建前端资源 "`)
  shell.exec('git push')
  
  shell.exec('sleep 10')
}

function copyFile() {
  
  const config = userConfig
  // web项目静态资源路径
  const resourcePath = argvData.resourcePath || config.targetRootPath
  
  // 复制文件
  let distOriginPath = path.resolve(__dirname, '../dist');
  let distTargetPath = `${resourcePath}/pc-fe`;
  let jspTargetPath = `${resourcePath}/dist`;
  
  // 复制jsp
  // shell.rm('-rf', jspTargetPath);
  // shell.mkdir('-p', jspTargetPath);
  
  // 复制静态资源
  shell.rm('-rf', distTargetPath);
  shell.mkdir('-p', distTargetPath);
  shell.cp('-r', `${distOriginPath}/*`, distTargetPath);
}

function customInjectFunc(template) {
  // #{} => ${}
  template = template.replace(/#\{(.*?)\}/g, '${$1}')
  // 注入构建信息
  template += `\n<!-- build on ${new Date().toLocaleString()}. -->`
  // 注入监控脚本
  template = template.replace('</head>', `${monitorScript}</head>`)
}

/** 
 * @description 校验
*/
function validate(path) {
  
  validateAllowBuildBranch()
  
  validateNotAllowBuildBranch()
  
  validateShbWebResourcePathExist(path)
  
  validateShbWebResourcePathIsGit(path)
  
  validateShbWebResourceGitHandler(path)
  
}

/** 
 * @description 校验 允许构建分支
*/
function validateAllowBuildBranch() {
  
  const allowBranch = argvData.allowBranch
  
  // 允许构建分支为空，不校验
  if (!allowBranch) {
    return
  }
  
  const currentBranch = getCurrentBranch()
  
  // 当前分支不在允许构建分支列表中
  if (!currentBranch.includes(allowBranch)) {
    
    console.log(`${
      colors.red(`
      ERROR: 当前分支为 ${currentBranch}, 允许构建分支为 ${allowBranch}，请切换到允许构建分支后重试！`)
    }`)
    
    exit()
    
    return
    
  }
  
  return
}

/** 
 * @description 校验 不允许构建分支
*/
function validateNotAllowBuildBranch() {
  
  const notAllowBranch = argvData.notAllowBranch
  
  // 不允许构建分支为空，不校验
  if (!notAllowBranch) {
    return
  }
  
  const currentBranch = getCurrentBranch()
  
  // 当前分支在不允许构建分支列表中
  if (currentBranch.includes(notAllowBranch)) {
    
    console.log(`${
      colors.red(`
      ERROR: 当前分支为 ${currentBranch}, 不允许构建分支为 ${notAllowBranch}，请切换到允许构建分支后重试！`)
    }`)
    
    exit()
    
    return
    
  }
  
  return
}

/** 
 * @description 校验 shb-web 静态资源路径是否存在
*/
function validateShbWebResourcePathExist(path) {
  if (!path) {
    console.log(`${
      colors.red(`
      ERROR: 没有找到对应的 shb-web-resource 仓库资源目录 !!!!!!!!
      请确保 /script/config/ 目录下对应用户配置下包含 targetRootPath 属性`)
    }`)
    exit()
  }
}

/** 
 * @description 校验 shb-web-resource 静态资源路径是否为 git仓库
*/
function validateShbWebResourcePathIsGit(path) {
  
  const isDir = shell.test('-d', path)
  
  if (!isDir) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性为目录路径 !!!!!!!!`)
    }`)
    exit()
  }
  
  shell.cd(path)
  
  const isGit = shell.exec(GIT_EXIST, { silent : true }).trim() == 'true'
  
  if (!isGit) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性目录路径为 git仓库 !!!!!!!!`)
    }`)
    exit()
  }
  
}

/** 
 * @description 校验 shb-web-resource git相关的操作
*/
function validateShbWebResourceGitHandler(path) {
  
  shell.cd(path)
  
  const isNotGitClean = shell.exec(GIT_STATUS_PORCELAIN, { silent : true }).trim()
  
  if (isNotGitClean) {
    console.log(`${
      colors.red(`
      ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性对应的 git 仓库 工作区是干净的`)
    }`)
    exit()
  }
  
  const currentShbWebResourceBranch = shell.exec(GIT_CURRENT_BRANCH_COMMAND, { silent : true }).trim()
  
  shell.cd(`${__dirname}/../`)
  
  const currentBranch = command(GIT_CURRENT_BRANCH_COMMAND).trim()
  const isAllotNotSameBranch = argvData.sameBranch === 'false'
  
  if (isAllotNotSameBranch) {
    return
  }
  
  if (currentBranch != currentShbWebResourceBranch) {
    console.log(`${
      colors.red(`
  ERROR: 请确保 /script/config/ 目录下对应用户配置 targetRootPath 属性对应的 git 仓库分支 与 当前 shb-pc-fe 的分支是一致的 ！！！！！
  如果需要开启不一致的分支，请在当前 npm 执行的脚本对应的后面的命令后面加上 --sameBranch=false 即可。
  
  示例：
  "build:spa:hbc": "node --max_old_space_size=24496 script/build-spa.js --user=huangbc --sameBranch=false --immediately"
  `)
}`)
    exit()
  }
  
}

// 命令行参数 含有 立即执行的参数 时，立即执行打包
if (argvData.immediately) {
  
  const config = userConfig
  // web项目静态资源路径
  const path = argvData.resourcePath || config.targetRootPath
  
  let buildOptions = new BuildOption()
  
  buildOptions.isCloudBuild = argvData.cloud;
  buildOptions.path = path;
  
  console.log('buildOptions: ', buildOptions)
  
  // 校验
  validate(path)
  
  !isAnalyzer && notificationStart('单页面 全量构建 ~~~~~~')
  
  // 是否开启 vite 构建
  const isVite = argvData.vite
  
  if (isVite) {
    productionViteSpaBuild(buildOptions)
    return
  }
  
  productionSpaBuild(buildOptions)
  
}

exports.productionSpaBuild = productionSpaBuild